/*
 * Copyright 2009 BioTeam Inc
 * 
 * Licensed under License GNU LESSER GENERAL PUBLIC LICENSE version 3.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 * 
 * http://www.gnu.org/copyleft/lesser.html
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */
package net.bioteam.appweb.ws;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.List;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.wsdl.Definition;
import javax.wsdl.extensions.schema.Schema;
import javax.wsdl.factory.WSDLFactory;
import javax.wsdl.xml.WSDLReader;
import javax.wsdl.xml.WSDLWriter;
import javax.xml.namespace.QName;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;

import net.bioteam.appweb.model.Parameter;
import net.bioteam.appweb.model.ParameterType;
import net.bioteam.appweb.model.Program;
import net.bioteam.appweb.model.ProgramBuilder;
import net.bioteam.appweb.model.Value;
import net.bioteam.appweb.model.meta.Entry;

import org.apache.ws.commons.schema.XmlSchema;
import org.apache.ws.commons.schema.XmlSchemaCollection;
import org.apache.ws.commons.schema.XmlSchemaComplexType;
import org.apache.ws.commons.schema.XmlSchemaElement;
import org.apache.ws.commons.schema.XmlSchemaEnumerationFacet;
import org.apache.ws.commons.schema.XmlSchemaLengthFacet;
import org.apache.ws.commons.schema.XmlSchemaMaxExclusiveFacet;
import org.apache.ws.commons.schema.XmlSchemaMinExclusiveFacet;
import org.apache.ws.commons.schema.XmlSchemaSequence;
import org.apache.ws.commons.schema.XmlSchemaSimpleType;
import org.apache.ws.commons.schema.XmlSchemaSimpleTypeRestriction;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.xml.sax.InputSource;

public class WsdlServlet extends HttpServlet {
	public static final String NS = "http://net.bioteam.appweb/ws";
	public static final String XSD_NS = "http://www.w3.org/2001/XMLSchema";

	@Override
	protected void doGet(HttpServletRequest req, HttpServletResponse resp)
			throws ServletException, IOException {
		String programName = req.getParameter("program");
		try {
			resp.setContentType("text/xml");
			createWsdl(programName, resp.getOutputStream());
		} catch (Exception e) {
			throw new ServletException(e);
		}
	}

	public static void createWsdl(String programName, OutputStream out) throws Exception
	{

			WSDLFactory factory = WSDLFactory.newInstance();
			WSDLReader reader = factory.newWSDLReader();
			reader.setFeature("javax.wsdl.verbose", false);
			reader.setFeature("javax.wsdl.importDocuments", false);
			InputStream in = WsdlServlet.class.getClassLoader()
					.getResourceAsStream("net/bioteam/appweb/ws/sample.wsdl");
			InputSource src = new InputSource(in);
			Definition def = reader.readWSDL(null, src);
			Schema schema = (Schema) def.getTypes().getExtensibilityElements()
					.get(0);
			Element schemaElement = schema.getElement();

			XmlSchemaCollection schemaCollection = new XmlSchemaCollection();
			XmlSchema xmlSchema = schemaCollection.read(schemaElement);
			QName name = new QName(NS, "parameters");
			//XmlSchemaElement programElement = 
			XmlSchemaComplexType programType = (XmlSchemaComplexType) xmlSchema.getTypeByName(name);
			XmlSchemaSequence programSeq = (XmlSchemaSequence) programType
					.getParticle();
			XmlSchemaElement nameElement = (XmlSchemaElement) programSeq.getItems()
            .getItem(0);
			nameElement.setFixedValue(programName);
			
//			XmlSchemaElement clusterElement = new XmlSchemaElement();
//			clusterElement.setName(Constant.progranm_form_cluster_name);
//			clusterElement.setMinOccurs(0);
//			clusterElement.setMaxOccurs(1);
//			clusterElement.setSchemaTypeName(new QName(XSD_NS, "integer", "xsd"));
//			programSeq.getItems().add(clusterElement);
			
			Program program = ProgramBuilder.getProgram(programName);
			for (Parameter parameter : program.getAllParameters()) {
				if (parameter.isHidden()) {
					continue;
				}

				if (parameter.getType().equals(ParameterType.ResultsType)) {
					continue;
				}

				boolean mandatory = false;

				if (parameter.isMandatory()
						&& (parameter.getDeaultValue() == null)) {
					mandatory = true;
				}

				XmlSchemaElement elt1 = new XmlSchemaElement();
				elt1.setName(parameter.getName());

				if (!mandatory) {
					elt1.setMinOccurs(0);
					elt1.setNillable(true);
				} else {
					elt1.setMinOccurs(1);
					elt1.setNillable(false);
				}

				elt1.setMaxOccurs(1);
				Object defValue=parameter.getDefaultValueAsObject();
				if (defValue!=null)
				{
					elt1.setDefaultValue(defValue.toString());
				}
				if (parameter.getType().equals(ParameterType.FloatType)) {
					if ((parameter.getScalemin() != null)
							|| (parameter.getScalemax() != null)) {
						QName typeName = createFloatType(xmlSchema, parameter
								.getName(), parameter.getScalemin(), parameter
								.getScalemax());
						elt1.setSchemaTypeName(typeName);
					} else {
						QName fType = new QName(XSD_NS, "double", "xsd");
						elt1.setSchemaTypeName(fType);
					}

					programSeq.getItems().add(elt1);
				} else if (parameter.getType()
						.equals(ParameterType.IntegerType)) {
					if ((parameter.getScalemin() != null)
							|| (parameter.getScalemax() != null)) {
						QName typeName = createIntegerType(xmlSchema, parameter
								.getName(), parameter.getScalemin(), parameter
								.getScalemax());
						elt1.setSchemaTypeName(typeName);
					} else {
						QName fType = new QName(XSD_NS, "integer", "xsd");
						elt1.setSchemaTypeName(fType);
					}

					programSeq.getItems().add(elt1);
				} else if (parameter.getType().equals(ParameterType.StringType)
						|| parameter.getType().equals(
								ParameterType.OutputFileType)) {
					QName fType = new QName(XSD_NS, "string", "xsd");
					elt1.setSchemaTypeName(fType);
					programSeq.getItems().add(elt1);
				} else if (parameter.getType()
						.equals(ParameterType.BooleanType)) {
					QName fType = new QName(XSD_NS, "boolean", "xsd");
					elt1.setSchemaTypeName(fType);
					programSeq.getItems().add(elt1);
				} else if (parameter.getType().equals(
						ParameterType.SingleSelectionType)) {
					QName typeName = createStringType(xmlSchema, parameter
							.getName(), parameter.getVlist().getValue(), 0);
					elt1.setSchemaTypeName(typeName);
					programSeq.getItems().add(elt1);
				} else if (parameter.getType().equals(
						ParameterType.MultipleSelectionType)) {
					elt1.setMaxOccurs(parameter.getVlist().getValue().size());

					QName typeName = createStringType(xmlSchema, parameter
							.getName(), parameter.getVlist().getValue(), 0);
					elt1.setSchemaTypeName(typeName);
					programSeq.getItems().add(elt1);
				} else if (parameter.getType().equals(
						ParameterType.InputFileType)
						|| parameter.getType().equals(
								ParameterType.SequenceType)) {
					QName typeName = new QName(NS, "fileType", "tns");
					elt1.setSchemaTypeName(typeName);
					programSeq.getItems().add(elt1);
				}
			}
			
			//file.
			for (int i=1; i<=10; i++)
			{
				XmlSchemaElement elt1 = new XmlSchemaElement();
				elt1.setName("_infile"+i);
				elt1.setMinOccurs(0);
				elt1.setNillable(true);
				elt1.setMaxOccurs(1);
				QName typeName = new QName(NS, "fileType", "tns");
				elt1.setSchemaTypeName(typeName);
				programSeq.getItems().add(elt1);
			}
			
			//run meta
			for (Entry entry:program.getMetaData().values())
			{
				if (entry.isDisplay())
				{
					XmlSchemaElement elt1 = new XmlSchemaElement();
					elt1.setName(entry.getName());
					elt1.setMinOccurs(0);
					elt1.setNillable(true);
					elt1.setMaxOccurs(1);
					QName fType = new QName(XSD_NS, "string", "xsd");
					elt1.setSchemaTypeName(fType);
					programSeq.getItems().add(elt1);
				}
			}

			/*
			 * <simpleType name="MyInt"> <restriction base="integer">
			 * <minExclusive value="0" /> <maxExclusive value="120" />
			 * </restriction> </simpleType>
			 */
			ByteArrayOutputStream byteOut = new ByteArrayOutputStream();
			xmlSchema.write(byteOut);

			// System.out.println(nameElement.getElementType());
			ByteArrayInputStream byteIn = new ByteArrayInputStream(byteOut
					.toByteArray());

			DocumentBuilderFactory domFactory = DocumentBuilderFactory
					.newInstance();
			DocumentBuilder builder = domFactory.newDocumentBuilder();
			Document doc1 = builder.parse(byteIn);

			// System.out.println(doc1.getDocumentElement().getNodeName());
			Element newSchemaElement = doc1.getDocumentElement();
			schema.setElement(newSchemaElement);

			WSDLWriter writer = factory.newWSDLWriter();
			writer.writeWSDL(def, out);
		
	}
	
	
	
	protected static QName createFloatType(XmlSchema xmlSchema, String name, Value min,
			Value max) {
		XmlSchemaSimpleType simpleType = new XmlSchemaSimpleType(xmlSchema);
		simpleType.setName(name);

		XmlSchemaSimpleTypeRestriction restriction = new XmlSchemaSimpleTypeRestriction();
		QName baseType = new QName(XSD_NS, "double", "xsd");
		restriction.setBaseTypeName(baseType);

		if (max != null) {
			Double maxValue = Double.parseDouble(max.getValue());
			XmlSchemaMaxExclusiveFacet maxFacet = new XmlSchemaMaxExclusiveFacet(
					maxValue, true);
			restriction.getFacets().add(maxFacet);
		}

		if (min != null) {
			Double minValue = Double.parseDouble(min.getValue());
			XmlSchemaMinExclusiveFacet minFacet = new XmlSchemaMinExclusiveFacet(
					minValue, true);
			restriction.getFacets().add(minFacet);
		}

		simpleType.setContent(restriction);

		xmlSchema.getItems().add(simpleType);

		QName schemaTypeName = new QName(NS, name, "tns");
		xmlSchema.getSchemaTypes().add(schemaTypeName, simpleType);

		return schemaTypeName;
	}

	protected static QName createIntegerType(XmlSchema xmlSchema, String name, Value min,
			Value max) {
		XmlSchemaSimpleType simpleType = new XmlSchemaSimpleType(xmlSchema);
		simpleType.setName(name);

		XmlSchemaSimpleTypeRestriction restriction = new XmlSchemaSimpleTypeRestriction();
		QName baseType = new QName(XSD_NS, "integer", "xsd");
		restriction.setBaseTypeName(baseType);

		if (max != null) {
			Long maxValue = Long.parseLong(max.getValue());
			XmlSchemaMaxExclusiveFacet maxFacet = new XmlSchemaMaxExclusiveFacet(
					maxValue, true);
			restriction.getFacets().add(maxFacet);
		}

		if (min != null) {
			Long minValue = Long.parseLong(min.getValue());
			XmlSchemaMinExclusiveFacet minFacet = new XmlSchemaMinExclusiveFacet(
					minValue, true);
			restriction.getFacets().add(minFacet);
		}

		simpleType.setContent(restriction);

		xmlSchema.getItems().add(simpleType);

		QName schemaTypeName = new QName(NS, name, "tns");
		xmlSchema.getSchemaTypes().add(schemaTypeName, simpleType);

		return schemaTypeName;
	}

	protected static QName createStringType(XmlSchema xmlSchema, String name,
			List<String> values, int length) {
		XmlSchemaSimpleType simpleType = new XmlSchemaSimpleType(xmlSchema);
		simpleType.setName(name);

		XmlSchemaSimpleTypeRestriction restriction = new XmlSchemaSimpleTypeRestriction();
		QName baseType = new QName(XSD_NS, "string", "xsd");
		restriction.setBaseTypeName(baseType);

		if ((values != null) && (values.size() > 0)) {
			for (String value : values) {
				XmlSchemaEnumerationFacet facet = new XmlSchemaEnumerationFacet(
						value, true);
				restriction.getFacets().add(facet);
			}
		} else if (length != 0) {
			XmlSchemaLengthFacet facet = new XmlSchemaLengthFacet(length, true);
			restriction.getFacets().add(facet);
		}

		simpleType.setContent(restriction);

		xmlSchema.getItems().add(simpleType);

		QName schemaTypeName = new QName(NS, name, "tns");
		xmlSchema.getSchemaTypes().add(schemaTypeName, simpleType);

		return schemaTypeName;
	}

}
